// ShellPidl.cpp: implementation of the CShellPidl class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "ShellPidl.h"
#include <intshcut.h>
#include <subsmgr.h>
#include <ExDisp.h>

LPSHELLFOLDER CShCMSort::is_less_than_pidl::psf;

#define SF_DRAGDROP_FLAGS SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK;
UINT CF_SHELLURL = RegisterClipboardFormat(CFSTR_SHELLURL);

#define TPM_FLAGS               (TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD)

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CShellPidl::CShellPidl()
{
	SHGetMalloc(&m_pMalloc);
    SHGetDesktopFolder(&m_psfDesktop);
	ZeroMemory(&m_EmptyPidl,sizeof(ITEMIDLIST));
}

CShellPidl::~CShellPidl()
{
	if (m_pMalloc)
		m_pMalloc->Release();
	if (m_psfDesktop)
		m_psfDesktop->Release();
}

IMalloc *CShellPidl::GetMalloc()
{
	return m_pMalloc;
}

LPITEMIDLIST CShellPidl::GetFullyQualPidl(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi)
{
	char szBuff[MAX_PATH];
	OLECHAR szOleChar[MAX_PATH];
	LPSHELLFOLDER lpsfDeskTop;
	LPITEMIDLIST  lpifq;
	ULONG ulEaten, ulAttribs;
	HRESULT hr;

	if (!GetName(lpsf, lpi, SHGDN_FORPARSING, szBuff))
	  return NULL;

	hr=SHGetDesktopFolder(&lpsfDeskTop);

	if (FAILED(hr))
	  return NULL;

	MultiByteToWideChar(CP_ACP,
					   MB_PRECOMPOSED,
					   szBuff,
					   -1,
					   (LPWSTR)szOleChar,
					   sizeof(szOleChar));

	hr=lpsfDeskTop->ParseDisplayName(NULL,
									NULL,
									szOleChar,
									&ulEaten,
									&lpifq,
									&ulAttribs);

	lpsfDeskTop->Release();

	if (FAILED(hr))
	  return NULL;

	return lpifq;
}

LPCITEMIDLIST CShellPidl::GetEmptyPidl()
{
	return &m_EmptyPidl;
}

bool CShellPidl::IsDesktopFolder(LPSHELLFOLDER psFolder)
{
	return psFolder == NULL || psFolder == m_psfDesktop;
}

BOOL CShellPidl::DoShellContectMenu(HWND hWnd, LPSHELLFOLDER lpsfParent, LPITEMIDLIST  lpi, LPPOINT lppt)
{
    LPCONTEXTMENU		lpcm;
    HRESULT				hr;
    char				szTemp[64];
    CMINVOKECOMMANDINFO cmi;
    DWORD               dwAttribs=0;
    int                 idCmd;
    HMENU               hMenu;
    BOOL                bSuccess=TRUE;

    hr=lpsfParent->GetUIObjectOf(hWnd,
        1,  // get attributes for this many objects
        (const struct _ITEMIDLIST **)&lpi,
        IID_IContextMenu,
        0,
        (LPVOID *)&lpcm);
    if (SUCCEEDED(hr))  
    {
		hMenu = CreatePopupMenu();

		if (hMenu)
		{
			// Get the context menu for the item.
			hr = lpcm->QueryContextMenu(hMenu, 0, 1, 0x7fff, CMF_EXPLORE);
			if (SUCCEEDED(hr))
			{
				idCmd=TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON, lppt->x, lppt->y, 0, hWnd, NULL);

				if (idCmd)
				{
					// Execute the command that was selected.
					cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
					cmi.fMask  = 0;
					cmi.hwnd   = hWnd;
					cmi.lpVerb = MAKEINTRESOURCE(idCmd-1);
					cmi.lpParameters = NULL;
      				cmi.lpDirectory  = NULL;
					cmi.nShow        = SW_SHOWNORMAL;
					cmi.dwHotKey     = 0;
					cmi.hIcon        = NULL;

					hr = lpcm->InvokeCommand(&cmi);
					if (!SUCCEEDED(hr))  
					{
					   wsprintf(szTemp, "InvokeCommand failed. hr = %lx", hr);
					   AfxMessageBox(szTemp);
					}
				}
			}
			else
				bSuccess = FALSE;

			DestroyMenu(hMenu);
		}
		else
			bSuccess = FALSE;

		lpcm->Release();
    } 
    else
	{
		wsprintf(szTemp, "GetUIObjectOf failed! hr = %lx", hr);
		AfxMessageBox(szTemp );
		bSuccess = FALSE;
    }
    return bSuccess;
}

void CShellPidl::DoDefaultShellContectMenuCommand(HWND hWnd, LPSHELLFOLDER lpsfParent, LPITEMIDLIST lpi)
{
    LPCONTEXTMENU		lpcm;
    HRESULT				hr;
    char				szTemp[64];
    CMINVOKECOMMANDINFO cmi;
    DWORD               dwAttribs=0;
    int                 idCmd;
    HMENU               hMenu;

    hr=lpsfParent->GetUIObjectOf(hWnd,
        1,  // get attributes for this many objects
        (const struct _ITEMIDLIST **)&lpi,
        IID_IContextMenu,
        0,
        (LPVOID *)&lpcm);
    if (SUCCEEDED(hr))  
    {
		hMenu = CreatePopupMenu();

		if (hMenu)
		{
			// Get the context menu for the item.
			hr = lpcm->QueryContextMenu(hMenu, 0, 1, 0x7fff, CMF_EXPLORE);
			if (SUCCEEDED(hr))
			{
				idCmd = ::GetMenuDefaultItem(hMenu, FALSE, GMDI_USEDISABLED);

				if (idCmd)
				{
					// Execute the command that was selected.
					cmi.cbSize			= sizeof(CMINVOKECOMMANDINFO);
					cmi.fMask			= 0;
					cmi.hwnd			= hWnd;
					cmi.lpVerb			= MAKEINTRESOURCE(idCmd-1);
					cmi.lpParameters	= NULL;
      				cmi.lpDirectory		= NULL;
					cmi.nShow			= SW_SHOWNORMAL;
					cmi.dwHotKey		= 0;
					cmi.hIcon			= NULL;

					hr = lpcm->InvokeCommand(&cmi);
					if (!SUCCEEDED(hr))  
					{
					   wsprintf(szTemp, "InvokeCommand failed. hr = %lx", hr);
					   AfxMessageBox(szTemp);
					}
				}
			}
			DestroyMenu(hMenu);
		}
		lpcm->Release();
    } 
    else
	{
		wsprintf(szTemp, "GetUIObjectOf failed! hr = %lx", hr);
		AfxMessageBox(szTemp );
    }
}

void CShellPidl::DoLastShellContectMenuCommand(HWND hWnd, LPSHELLFOLDER lpsfParent, LPITEMIDLIST lpi)
{
    LPCONTEXTMENU		lpcm;
    HRESULT				hr;
    char				szTemp[64];
    CMINVOKECOMMANDINFO cmi;
    DWORD               dwAttribs=0;
    int                 idCmd;
    HMENU               hMenu;

    hr=lpsfParent->GetUIObjectOf(hWnd,
        1,  // get attributes for this many objects
        (const struct _ITEMIDLIST **)&lpi,
        IID_IContextMenu,
        0,
        (LPVOID *)&lpcm);
    if (SUCCEEDED(hr))  
    {
		hMenu = CreatePopupMenu();

		if (hMenu)
		{
			// Get the context menu for the item.
			hr = lpcm->QueryContextMenu(hMenu, 0, 1, 0x7fff, CMF_EXPLORE);
			if (SUCCEEDED(hr))
			{
				idCmd = ::GetMenuItemID(hMenu, GetMenuItemCount(hMenu)-1);

				if (idCmd)
				{
					// Execute the command that was selected.
					cmi.cbSize			= sizeof(CMINVOKECOMMANDINFO);
					cmi.fMask			= 0;
					cmi.hwnd			= hWnd;
					cmi.lpVerb			= MAKEINTRESOURCE(idCmd-1);
					cmi.lpParameters	= NULL;
      				cmi.lpDirectory		= NULL;
					cmi.nShow			= SW_SHOWNORMAL;
					cmi.dwHotKey		= 0;
					cmi.hIcon			= NULL;

					hr = lpcm->InvokeCommand(&cmi);
					if (!SUCCEEDED(hr))  
					{
					   wsprintf(szTemp, "InvokeCommand failed. hr = %lx", hr);
					   AfxMessageBox(szTemp);
					}
				}
			}
			DestroyMenu(hMenu);
		}
		lpcm->Release();
    } 
    else
	{
		wsprintf(szTemp, "GetUIObjectOf failed! hr = %lx", hr);
		AfxMessageBox(szTemp );
    }
}

LPSHELLFOLDER CShellPidl::GetDesktopFolder()
{
	return m_psfDesktop;
}

LPSHELLFOLDER CShellPidl::GetFolder(LPITEMIDLIST pidl)
{
	if (pidl == NULL || pidl->mkid.cb == 0)
		return m_psfDesktop;
	LPSHELLFOLDER pFolder=NULL;
	if (FAILED(m_psfDesktop->BindToObject(pidl, 0, IID_IShellFolder,(LPVOID*)&pFolder)))
		return NULL;
	return pFolder;
}

// CopyItemID - creates an item identifier list containing the first 
//     item identifier in the specified list. 
// Returns a PIDL if successful, or NULL if out of memory. 
LPITEMIDLIST CShellPidl::CopyItemID(LPITEMIDLIST pidl,int n) 
{ 
	// Get the size of the specified item identifier. 
	ASSERT(pidl);
	if (n == 0)
	{
		int cb = pidl->mkid.cb;
		int nSize = cb + sizeof(pidl->mkid.cb);
		// Allocate a new item identifier list. 
		LPITEMIDLIST pidlNew = (LPITEMIDLIST)m_pMalloc->Alloc(nSize); 
		ZeroMemory(pidlNew,nSize);
		if (pidlNew == NULL) 
			return NULL; 
		// Copy the specified item identifier. 
		CopyMemory(pidlNew, pidl, nSize-sizeof(pidl->mkid.cb)); 
		return pidlNew; 
	}
	else
	{
		LPITEMIDLIST pidl_index=NULL;
  		for(int i=0;i < n && pidl->mkid.cb;i++)
		{
			pidl_index = pidl;
			pidl = Next(pidl);
		}
		return pidl_index ? CopyItemID(pidl_index,0) : NULL;
	}
	return NULL;
}

// Returns a PIDL if successful, or NULL if out of memory. 
LPITEMIDLIST CShellPidl::CopyLastItemID(LPITEMIDLIST pidl) 
{ 
	// Get the size of the specified item identifier. 
	ASSERT(pidl);
    if (pidl == NULL)
		return NULL;
	LPITEMIDLIST last_pidl=pidl;
    while (pidl->mkid.cb)
    {
		last_pidl = pidl;
        pidl = Next(pidl);
    }
	if (last_pidl == NULL)
		return NULL;
	return CopyItemID(last_pidl);
}

// copies the absolute pidl up till n
LPITEMIDLIST CShellPidl::CopyAbsItemID(LPITEMIDLIST pidl,int n) 
{ 
	// Get the size of the specified item identifier. 
	ASSERT(pidl);
    if (pidl == NULL)
		return NULL;
	LPITEMIDLIST first_pidl=NULL;
	LPITEMIDLIST abs_pidl=NULL;
	LPITEMIDLIST new_abs_pidl=NULL;
    for(int i=0;i < n && pidl && pidl->mkid.cb;i++)
    {
		first_pidl = CopyItemID(pidl);
		new_abs_pidl = ConcatPidl(abs_pidl,first_pidl);
		if (abs_pidl)
		{
			m_pMalloc->Free(abs_pidl);
		}
		abs_pidl = new_abs_pidl;
		if (first_pidl)
		{
			m_pMalloc->Free(first_pidl);
		}
        pidl = Next(pidl);
    }
	return new_abs_pidl;
}

// Makes a copy of an ITEMIDLIST 
LPITEMIDLIST CShellPidl::CopyItemIDList(LPITEMIDLIST pidl) 
{ 
	// Allocate a new item identifier list. 
	int nSize = GetSize(pidl);
	LPITEMIDLIST pidlNew = (LPITEMIDLIST)m_pMalloc->Alloc(nSize); 
	ZeroMemory(pidlNew,nSize);
	if (pidlNew == NULL) 
		return NULL; 
	// Copy the specified item identifier. 
	CopyMemory(pidlNew, pidl, nSize); 

	return pidlNew; 
}

bool CShellPidl::CompareMemPidls(LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2) 
{ 
	// Allocate a new item identifier list. 
	if (pidl1 == NULL || pidl2 == NULL)
		return false;
	return memcmp(pidl1,pidl2,(size_t)GetSize(pidl1)) == 0;
}

// Returns true if lists are the same
bool CShellPidl::ComparePidls(LPSHELLFOLDER pFolder,LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2) 
{ 
	// Allocate a new item identifier list. 
	if (pFolder == NULL)
		pFolder = GetDesktopFolder();
	if (pidl1 == NULL || pidl2 == NULL)
		return false;
	return (short)pFolder->CompareIDs(0,pidl1,pidl2) == 0;
}

void CShellPidl::Free(void *pv)
{
	if (m_pMalloc)
		m_pMalloc->Free(pv);
}

CString CShellPidl::GetLastWriteTimeString(LPCTSTR szFilePath)
{
	CString strTime;
	
	CFileStatus	fs;
	if(!CFile::GetStatus(szFilePath,fs))
		return "";

	CTime time = fs.m_mtime;

	strTime.Format( _T("%.4d-%.2d-%.2d %.2d:%.2d"), 
			time.GetYear(), time.GetMonth(), time.GetDay(), time.GetHour(), time.GetSecond());

	return strTime;
}

void WINAPI CShellPidl::OpenFileWith(CString szFileName)
{
	CString argument = "shell32.dll,OpenAs_RunDLL ";
	argument += szFileName;

	ShellExecute (NULL, "open", "rundll32.exe", (LPCTSTR)argument, NULL, SW_SHOW);
}

BOOL WINAPI CShellPidl::FileExist(LPCTSTR szFileName)
{
	CString	szFile = szFileName;

	if(szFile.GetAt(szFile.GetLength()-1)=='\\')
		szFile.Delete(szFile.GetLength()-1);

	WIN32_FIND_DATA	DATA;
	HANDLE Handle;
	Handle=::FindFirstFile(szFile,&DATA);
	if(Handle==INVALID_HANDLE_VALUE)
		return FALSE;
	::FindClose(Handle);
	return TRUE;
}

BOOL WINAPI CShellPidl::IsDirectory(LPCTSTR szPathName)
{
	CString szTempPath;
	::GetCurrentDirectory(256,szTempPath.GetBuffer(256));

	BOOL bRet = ::SetCurrentDirectory(szPathName);
	::SetCurrentDirectory(szTempPath);

	return bRet;
}

CString CShellPidl::GetFileSizeString(LPCTSTR szFilePath)
{
	CString strFileSize;
	CString strTemp;

	CFileStatus	fs;
	if(!CFile::GetStatus(szFilePath,fs))
		return _T("");

	if(fs.m_attribute & CFile::directory)
		return _T("");
	
	strTemp.Format(_T("%d"), fs.m_size/1024+1);
	int nSize = strTemp.GetLength()-1;
	int nCount = 1;

	while (nSize >= 0)
	{
		strFileSize += strTemp.GetAt(nSize);

		if (nSize  == 0)
			break;
		if (nCount != 3)
			++nCount;
		else 
		{
			strFileSize += _T(",");
			nCount = 1;
		}
		nSize--;
	}

	strFileSize.MakeReverse();
	strFileSize += " KB";
	return strFileSize;
}

void CShellPidl::FreePidl(LPITEMIDLIST pidl)
{
	if (m_pMalloc)
		m_pMalloc->Free(pidl);
}

UINT CShellPidl::GetItemIdCount(LPCITEMIDLIST pidl)
{
    UINT nCount = 0;
    if (pidl)
    {
        while (pidl->mkid.cb)
        {
            pidl = Next(pidl);
			nCount++;
        }
    }
    return nCount;
}

UINT CShellPidl::GetSize(LPCITEMIDLIST pidl)
{
    UINT cbTotal = 0;
    if (pidl)
    {
        cbTotal += sizeof(pidl->mkid.cb);       // Null terminator
        while (pidl->mkid.cb)
        {
            cbTotal += pidl->mkid.cb;
            pidl = Next(pidl);
        }
    }
    return cbTotal;
}

LPITEMIDLIST CShellPidl::Next(LPCITEMIDLIST pidl)
{
   LPSTR lpMem=(LPSTR)pidl;

   lpMem+=pidl->mkid.cb;

   return (LPITEMIDLIST)lpMem;
}

LPITEMIDLIST CShellPidl::ConcatPidl(LPITEMIDLIST pidlDest,LPITEMIDLIST pidlSrc) 
{ 
	// Get the size of the specified item identifier. 
    UINT cbDest=0;
    UINT cbSrc=0;
    if (pidlDest)  //May be NULL
       cbDest = GetSize(pidlDest) - sizeof(pidlDest->mkid.cb);
    cbSrc = GetSize(pidlSrc);

	// Allocate a new item identifier list. 
	LPITEMIDLIST pidlNew = (LPITEMIDLIST)m_pMalloc->Alloc(cbSrc+cbDest); 
	if (pidlNew == NULL) 
		return NULL; 
	ZeroMemory(pidlNew,cbSrc+cbDest);
	// Copy the specified item identifier. 
	if (pidlDest)
		CopyMemory(pidlNew, pidlDest, cbDest); 
	CopyMemory(((USHORT*)(((LPBYTE)pidlNew)+cbDest)), pidlSrc, cbSrc); 

	return pidlNew; 
}

int CShellPidl::GetShellIcon( CString& sName, BOOL bFileExist, BOOL bOpen, BOOL bSmall)
{
	SHFILEINFO sfi = {0};
    
	DWORD dwFlags = SHGFI_SYSICONINDEX;
	size_t size = sizeof( SHFILEINFO );

	if( bSmall )
		dwFlags |= SHGFI_SMALLICON;
    
    if( bOpen )
		dwFlags |= SHGFI_OPENICON;

	if( !bFileExist )
		dwFlags |= SHGFI_USEFILEATTRIBUTES;
	else
		dwFlags &= ~SHGFI_USEFILEATTRIBUTES;
 
    SHGetFileInfo( sName, FILE_ATTRIBUTE_NORMAL, &sfi, size, dwFlags );
    return( sfi.iIcon );
}

int CShellPidl::GetShellIcon(LPITEMIDLIST lpi, UINT uFlags)
{
   SHFILEINFO    sfi;
   ZeroMemory(&sfi,sizeof(sfi));
   if (uFlags == 0)
		uFlags |= (SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
   uFlags |= SHGFI_PIDL;
   SHGetFileInfo((LPCTSTR)lpi, 0, &sfi, sizeof(SHFILEINFO), uFlags);

   return sfi.iIcon;
}

STDMETHODIMP CShellPidl::SHPidlToPathEx(LPCITEMIDLIST pidl, CString &sPath, LPSHELLFOLDER pFolder, DWORD dwFlags)
{
	STRRET StrRetFilePath;
	LPTSTR pszFilePath = NULL;
	HRESULT hr=E_FAIL;
	if (pFolder == NULL)
		pFolder = GetDesktopFolder();
	if (pFolder == NULL)
		return E_FAIL;
	hr = pFolder->GetDisplayNameOf(pidl, dwFlags, &StrRetFilePath);
	if (SUCCEEDED(hr))
	{
		StrRetToStr(StrRetFilePath, &pszFilePath, (LPITEMIDLIST)pidl);
		sPath = pszFilePath;
	}
	if (pszFilePath)
		m_pMalloc->Free(pszFilePath);
	return hr;
}

STDMETHODIMP CShellPidl::SHPathToPidlEx(LPCTSTR szPath, LPITEMIDLIST* ppidl, LPSHELLFOLDER pFolder)
{
   OLECHAR wszPath[MAX_PATH] = {0};
   ULONG nCharsParsed = 0;
   LPSHELLFOLDER pShellFolder = NULL;
   BOOL bFreeOnExit = FALSE;
#ifdef UNICODE
   lstrcpy(wszPath,szPath);
#else
   MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szPath, -1, wszPath, MAX_PATH);
#endif
   // Use the desktop's IShellFolder by default
   if(pFolder == NULL)
   {
      SHGetDesktopFolder(&pShellFolder);
      bFreeOnExit = TRUE;
   }
   else
      pShellFolder = pFolder;

   HRESULT hr = pShellFolder->ParseDisplayName(NULL, NULL, wszPath, &nCharsParsed, ppidl, NULL);

   if(bFreeOnExit)
      pShellFolder->Release();

   return hr;
}

void CShellPidl::GetTypeName(LPITEMIDLIST lpi,CString &sTypeName)
{
	SHFILEINFO    sfi;
	ZeroMemory(&sfi,sizeof(sfi));
	UINT uFlags = SHGFI_PIDL | SHGFI_TYPENAME;
	SHGetFileInfo((LPCTSTR)lpi, 0, &sfi, sizeof(SHFILEINFO), uFlags);
	sTypeName = sfi.szTypeName;
}

CString CShellPidl::GetTypeName(LPCTSTR szFilePath, BOOL bFile)
{
	SHFILEINFO	sfi;
	ZeroMemory(&sfi,sizeof(sfi));

	UINT uFlags = SHGFI_TYPENAME;
	if(bFile)
		uFlags |= SHGFI_USEFILEATTRIBUTES;

	::SHGetFileInfo((LPCTSTR)szFilePath, 0, &sfi, sizeof(SHFILEINFO), uFlags);
	return sfi.szTypeName;
}

void CShellPidl::GetDisplayName(LPITEMIDLIST lpifq,CString &sDisplayName)
{
   SHFILEINFO    sfi;
   ZeroMemory(&sfi,sizeof(sfi));
   UINT uFlags = SHGFI_PIDL | SHGFI_DISPLAYNAME;
   SHGetFileInfo((LPCTSTR)lpifq, 0, &sfi, sizeof(SHFILEINFO), uFlags);
   sDisplayName = sfi.szDisplayName;
}

void CShellPidl::GetNormalAndSelectedIcons (
   LPITEMIDLIST lpifq, int &iImage, int &iSelectedImage)
{
   // Don't check the return value here. 
   // If IGetIcon() fails, you're in big trouble.
   iImage = GetShellIcon (lpifq, 
      SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
   
   iSelectedImage = GetShellIcon (lpifq, 
      SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_OPENICON);
   
   return;
}

BOOL CShellPidl::HandleMenuMsg(HWND hwnd, LPSHELLFOLDER lpsfParent,
     LPITEMIDLIST  lpi, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LPCONTEXTMENU lpcm=NULL;
    HRESULT hr=lpsfParent->GetUIObjectOf(hwnd,
        1,  // get attributes for this many objects
        (const struct _ITEMIDLIST **)&lpi,
        IID_IContextMenu,
        0,
        (LPVOID *)&lpcm);
    if (SUCCEEDED(hr))  
    {
	    LPCONTEXTMENU2 lpcm2=NULL;
		hr = lpcm->QueryInterface(IID_IContextMenu2,(LPVOID*)&lpcm2);
		lpcm->Release();
		if (SUCCEEDED(hr))  
	    {
			lpcm2->HandleMenuMsg(uMsg,wParam,lParam);
			lpcm2->Release();
		}
		return TRUE;
	}
	return FALSE;
}

BOOL CShellPidl::PopupTheMenu(HWND hwnd, LPSHELLFOLDER lpsfParent,
     LPITEMIDLIST  *plpi, UINT cidl, LPPOINT lppt)
{
	/*
 	CMenu menu;
    menu.CreatePopupMenu();
	g_CoolMenuManager.Install(CWnd::FromHandle(hwnd));
	CString sPath;
	if (lpsfParent == NULL)
		lpsfParent = GetDesktopFolder();
	SHPidlToPathEx(*plpi,sPath,lpsfParent);
	CShellContextMenu shell_menu(hwnd,sPath,plpi,cidl,lpsfParent);
	shell_menu.SetMenu(&menu);
    int idCmd = menu.TrackPopupMenu(TPM_FLAGS, lppt->x, lppt->y, CWnd::FromHandle(hwnd));
	shell_menu.InvokeCommand(idCmd);
	g_CoolMenuManager.Uninstall();
	*/
	return TRUE;
}

/****************************************************************************
*
*    FUNCTION: GetName(LPSHELLFOLDER lpsf,LPITEMIDLIST  lpi,DWORD dwFlags,
*             LPSTR         lpFriendlyName)
*
*    PURPOSE:  Gets the friendly name for the folder 
*
****************************************************************************/
BOOL CShellPidl::GetName (LPSHELLFOLDER lpsf, LPITEMIDLIST lpi, DWORD dwFlags, LPSTR sFriendlyName)
{
   BOOL   bSuccess=TRUE;
   STRRET str;

   if (NOERROR==lpsf->GetDisplayNameOf(lpi,dwFlags, &str))
   {
      switch (str.uType)
      {
         case STRRET_WSTR:
#ifdef UNICODE
			 _tcscpy(sFriendlyName, str.pOleStr);
#else
            WideCharToMultiByte(
               CP_ACP,					// code page
               0,						// dwFlags
               str.pOleStr,				// lpWideCharStr
               -1,						// cchWideCharStr
               sFriendlyName,			// lpMultiByteStr
               _MAX_PATH,				// cchMultiByte
               NULL,					// lpDefaultChar
               NULL);					// lpUsedDefaultChar
#endif
             break;

         case STRRET_OFFSET:
			 lstrcpy(sFriendlyName, (LPSTR)lpi+str.uOffset);
             break;

         case STRRET_CSTR:             
			 lstrcpy(sFriendlyName, (LPSTR)str.cStr);
             break;

         default:
             bSuccess = FALSE;
             break;
      }
   }
   else
      bSuccess = FALSE;

   return bSuccess;
}

//
// ResolveChannel: Resolves a Channel Shortcut to its URL
//
STDMETHODIMP CShellPidl::ResolveChannel(IShellFolder* pFolder, LPCITEMIDLIST pidl, LPTSTR* lpszURL)
{
   IShellLink* pShellLink;

   *lpszURL = NULL;  // Assume failure

   // Get a pointer to the IShellLink interface from the given folder
   HRESULT hr = pFolder->GetUIObjectOf(NULL, 1, &pidl, IID_IShellLink, NULL, (LPVOID*)&pShellLink);
   if (SUCCEEDED(hr))
   {
      LPITEMIDLIST pidlChannel;

      // Convert the IShellLink pointer to a PIDL.
      hr = pShellLink->GetIDList(&pidlChannel);
      if (SUCCEEDED(hr))
      {
         IShellFolder* psfDesktop;

         hr = SHGetDesktopFolder(&psfDesktop);
         if (SUCCEEDED(hr))
         {
            STRRET strret;

            hr = psfDesktop->GetDisplayNameOf(pidlChannel, 0, &strret);
            if (SUCCEEDED(hr))
				StrRetToStr(strret, lpszURL, pidlChannel);

            psfDesktop->Release();
         }
      }

      pShellLink->Release();
   }

   return hr;
}

STDMETHODIMP CShellPidl::ResolveHistoryShortcut(LPSHELLFOLDER pFolder,LPCITEMIDLIST *ppidl,CString &sURL)
{
	HRESULT hr=E_FAIL;
	IDataObject *pObj=NULL;
	hr = pFolder->GetUIObjectOf(NULL, 1, ppidl, IID_IDataObject, NULL, (LPVOID*)&pObj);
	if (SUCCEEDED(hr))
	{
		hr = GetURL(pObj,sURL);
		pObj->Release();
	}
	return hr;
}

STDMETHODIMP CShellPidl::GetURL(IDataObject *pDataObj,CString &sURL)
{
	sURL.Empty();
	STGMEDIUM stgm;
	ZeroMemory(&stgm, sizeof(stgm));

    FORMATETC fetc;
    fetc.cfFormat = CF_SHELLURL;
    fetc.ptd = NULL;
    fetc.dwAspect = DVASPECT_CONTENT;
    fetc.lindex = -1;
    fetc.tymed = TYMED_HGLOBAL;

    HRESULT hr = pDataObj->QueryGetData(&fetc);
	if (FAILED(hr))
		return hr;
    hr = pDataObj->GetData(&fetc, &stgm);
	if (FAILED(hr))
		return hr;
    LPCTSTR pData = (LPCTSTR)GlobalLock(stgm.hGlobal);
	sURL = pData;
	GlobalUnlock(stgm.hGlobal);
    ReleaseStgMedium(&stgm);
	return S_OK;
}

//
// ResolveInternetShortcut: Resolves an Internet Shortcut to its URL
//
STDMETHODIMP CShellPidl::ResolveInternetShortcut(LPCTSTR lpszLinkFile, LPTSTR* lpszURL)
{
	IUniformResourceLocator* pUrlLink = NULL;

	*lpszURL=NULL;

	HRESULT hr = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,
							 IID_IUniformResourceLocator, (LPVOID*)&pUrlLink);
	if (FAILED(hr))
		return hr;

	IPersistFile* pPersistFile = NULL;
	hr = pUrlLink->QueryInterface(IID_IPersistFile, (LPVOID*)&pPersistFile);
	if (SUCCEEDED(hr))
	{
		 // Ensure that the string is Unicode. 
		 WORD wsz[MAX_PATH];  
		#ifdef UNICODE
		 _tcscpy(wsz,lpszLinkFile);
		#else
		 MultiByteToWideChar(CP_ACP, 0, lpszLinkFile, -1, (LPWSTR)wsz, MAX_PATH);
		#endif
		 // Load the Internet Shortcut from persistent storage.
		 hr = pPersistFile->Load((LPCOLESTR)wsz, STGM_READ);
		 if (SUCCEEDED(hr))
		 {
			hr = pUrlLink->GetURL(lpszURL);
		 }
		 pPersistFile->Release();
	}
	pUrlLink->Release();

	return hr;
}  

//
// ResolveLink: Resolves a Shell Link to its actual folder location
//
STDMETHODIMP CShellPidl::ResolveLink(HWND hWnd,LPCTSTR lpszLinkFile, LPTSTR* lpszURL)
{
   IShellLink* pShellLink = NULL;

   *lpszURL = NULL;   // Assume failure

   HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
                                 IID_IShellLink, (LPVOID*)&pShellLink); 
   if (SUCCEEDED(hr))
   {
      IPersistFile* pPersistFile = NULL;

      hr = pShellLink->QueryInterface(IID_IPersistFile, (LPVOID*)&pPersistFile);
      if (SUCCEEDED(hr))
      {
         // Ensure that the string is Unicode. 
         WORD wsz[MAX_PATH];  
#ifdef UNICODE
		 _tcscpy(wsz,lpszLinkFile);
#else
         MultiByteToWideChar(CP_ACP, 0, lpszLinkFile, -1, (LPWSTR)wsz, MAX_PATH);
#endif

         // Load the shortcut.from persistent storage
         hr = pPersistFile->Load((LPCOLESTR)wsz, STGM_READ);
         if (SUCCEEDED(hr))
         {
            WIN32_FIND_DATA wfd;      

            hr = pShellLink->Resolve(hWnd, SLR_NO_UI); 
            if (NOERROR == hr)
            {
               // Get the path to the link target. 
	   		   *lpszURL = (LPTSTR)m_pMalloc->Alloc(MAX_PATH);  // Must remember to Free later

               hr = pShellLink->GetPath(*lpszURL, MAX_PATH - 1, (WIN32_FIND_DATA*)&wfd, SLGP_UNCPRIORITY);
            }
         }

         pPersistFile->Release();
      }

      pShellLink->Release();
   }

   return hr;
}

//
// This method converts a STRRET structure to a LPTSTR
//

STDMETHODIMP CShellPidl::StrRetToStr(STRRET StrRet, LPTSTR* str, LPITEMIDLIST pidl)
{

	HRESULT hr = S_OK;
	int cch;
	LPSTR strOffset;

	*str = NULL;  // Assume failure

	switch (StrRet.uType)
   {
		case STRRET_WSTR: 
			cch = WideCharToMultiByte(CP_ACP, 0, StrRet.pOleStr, -1, NULL, 0, NULL, NULL); 
			*str = (LPTSTR)m_pMalloc->Alloc(cch * sizeof(TCHAR)); 

			if (*str != NULL)
				WideCharToMultiByte(CP_ACP, 0, StrRet.pOleStr, -1, *str, cch, NULL, NULL); 
			else
				hr = E_FAIL;
			break;

		case STRRET_OFFSET: 
			strOffset = (((char *) pidl) + StrRet.uOffset);

			cch = strlen(strOffset) + 1; // NULL terminator
			*str = (LPTSTR)m_pMalloc->Alloc(cch * sizeof(TCHAR));

			if (*str != NULL)
				strcpy(*str, strOffset);
			else
				hr = E_FAIL;
			break;

		case STRRET_CSTR: 
			cch = strlen(StrRet.cStr) + 1; // NULL terminator
			*str = (LPTSTR)m_pMalloc->Alloc(cch * sizeof(TCHAR));

			if (*str != NULL)
				strcpy(*str, StrRet.cStr);
			else
				hr = E_FAIL;

			break;
	} 

	return hr;
}
